#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "nvs_flash.h"
#include "esp_netif.h"
#include <esp_http_server.h>

#include "cJSON.h"

#include "sgf_web.h"
#include "sgf_wifi.h"
#include "sys/sgf_cfg.h"

extern sgf_cfg_t sgf_cfg;
extern const char root_start[] asm("_binary_root_html_start");
extern const char root_end[] asm("_binary_root_html_end");

static const char* TAG = "web";
httpd_handle_t server = NULL;



static esp_err_t root_get_handler(httpd_req_t *req)
{
    const uint32_t root_len = root_end - root_start;

    ESP_LOGI(TAG, "Server root");
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, root_start, root_len);
    return ESP_OK;
}

static esp_err_t sgf_get_handler(httpd_req_t *req)
{
    ESP_LOGI(TAG, "sgf param get");
    httpd_resp_set_type(req, HTTPD_TYPE_JSON);
    char repjson[500] = { 0 };
    sprintf(repjson, "{\"wifi\": {\"ssid\": \"%s\", \"passwd\": \"%s\"},"
                        "\"http\": {\"key\": \"%s\", \"code\": \"%s\"},"
                        "\"gui\": {\"remind\": \"%s\"}, \"status\" : \"ok\"}", 
                        strlen(sgf_cfg.wifi.ssid)? sgf_cfg.wifi.ssid: "", strlen(sgf_cfg.wifi.passwd)? sgf_cfg.wifi.passwd: "", 
                        strlen(sgf_cfg.http.key)? sgf_cfg.http.key: "", strlen(sgf_cfg.http.code)? sgf_cfg.http.code: "",
                        strlen(sgf_cfg.gui.remind)? sgf_cfg.gui.remind: "");
    httpd_resp_sendstr(req, repjson);
    return ESP_OK;
}

static int response_root(httpd_req_t *req)
{
    httpd_resp_set_status(req, "302 Temporary Redirect");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN);
    return 0;
}

static esp_err_t sgf_post_handler(httpd_req_t *req)
{
    ESP_LOGI(TAG, "handler read content length %d", req->content_len);

    char*  buf = malloc(req->content_len + 1);
    size_t off = 0;
    int    ret;

    if (!buf) {
        ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", req->content_len + 1);
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    while (off < req->content_len) {
        ret = httpd_req_recv(req, buf + off, req->content_len - off);
        if (ret <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                httpd_resp_send_408(req);
            }
            free (buf);
            return ESP_FAIL;
        }
        off += ret;
    }
    buf[off] = '\0';
    ESP_LOGI(TAG, "body read %s", buf);
    
    cJSON *root = cJSON_Parse(buf);
    if (root) {
        cJSON *js_wifi = cJSON_GetObjectItem(root, "wifi");
        if (js_wifi) {
            cJSON *js_ssid = cJSON_GetObjectItem(js_wifi, "ssid");
            cJSON *js_passwd = cJSON_GetObjectItem(js_wifi, "passwd");
            char *ssid, *passwd;
            ssid = cJSON_GetStringValue(js_ssid);
            passwd = cJSON_GetStringValue(js_passwd);
            strcpy(sgf_cfg.wifi.passwd, passwd);
            strcpy(sgf_cfg.wifi.ssid, ssid);
        }
        cJSON *js_http = cJSON_GetObjectItem(root, "http");
        if (js_http) {
            cJSON *js_key = cJSON_GetObjectItem(js_http, "key");
            char *key;
            key = cJSON_GetStringValue(js_key);
            strcpy(sgf_cfg.http.key, key);
        }
        cJSON *js_gui = cJSON_GetObjectItem(root, "gui");
        if (js_gui) {
            cJSON *js_remind = cJSON_GetObjectItem(js_gui, "remind");
            char *remind;
            remind = cJSON_GetStringValue(js_remind);
            strcpy(sgf_cfg.gui.remind, remind);
        }
        sgf_cfg_write(&sgf_cfg);
        cJSON_Delete(root);
        vTaskDelay(pdMS_TO_TICKS(200));
        response_root(req);
    }
    return ESP_OK;
}

static const httpd_uri_t sgf_post = {
    .uri = "/sgf_post",
    .method = HTTP_POST,
    .handler = sgf_post_handler
};

static const httpd_uri_t sgf_get = {
    .uri = "/sgf",
    .method = HTTP_GET,
    .handler = sgf_get_handler
};

static const httpd_uri_t root = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = root_get_handler
};

// HTTP Error (404) Handler - Redirects all requests to the root page
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{
    httpd_resp_set_status(req, "302 Temporary Redirect");
    httpd_resp_set_hdr(req, "Location", "/");
    httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN);

    ESP_LOGI(TAG, "Redirecting to root");
    return ESP_OK;
}

static httpd_handle_t start_webserver(void)
{
    
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.max_open_sockets = 2;
    config.lru_purge_enable = true;

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &root);
        httpd_register_uri_handler(server, &sgf_post);
        httpd_register_uri_handler(server, &sgf_get);
        httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
    }
    return server;
}

int sgf_web_init(void)
{
    start_webserver();
    return 0;
}